#include "box.h"
#include <algorithm>
#include <math.h>

const double epsilon = 1e-6;

Box::Box()
{
    x0 = x1 = 0;
    y0 = y1 = 0;
    z0 = z1 = 0;
    status = BOX_UNKNOWN;
}

Box::Box(double _x0, double _x1, double _y0, double _y1, double _z0, double _z1)
{
    x0 = _x0;
    x1 = _x1;
    y0 = _y0;
    y1 = _y1;
    z0 = _z0;
    z1 = _z1;
    status = BOX_UNKNOWN;
}

Box::Box(const Box &b)
{
    x0 = b.x0;
    x1 = b.x1;
    y0 = b.y0;
    y1 = b.y1;
    z0 = b.z0;
    z1 = b.z1;
    status = b.status;
}

Box &Box::operator =(const Box &b)
{
    x0 = b.x0;
    x1 = b.x1;
    y0 = b.y0;
    y1 = b.y1;
    z0 = b.z0;
    z1 = b.z1;
    status = b.status;
}

void Box::draw()
{
    drawBox(*this);
}

double Box::width()
{
    return x1 - x0;
}

double Box::height()
{
    return y1 - y0;
}

bool Box::subdivide(Box *subBox, double threshold)
{
    if (isSmall(threshold))
    {
        return false;
    }

    double xm = (x0 + x1)/2;
    double ym = (y0 + y1)/2;
    double zm = (z0 + z1)/2;

    // binary tree
    if (y1 - y0 >= x1 - x0
            && y1 - y0 >= z1 - z0)
    {
        subBox[0] = Box(x0, x1, y0, ym, z0, z1);
        subBox[1] = Box(x0, x1, ym, y1, z0, z1);
    }
    else if (x1 - x0 >= z1 - z0
             && x1 - x0 >= y1 - y0)
    {
        subBox[0] = Box(x0, xm, y0, y1, z0, z1);
        subBox[1] = Box(xm, x1, y0, y1, z0, z1);
    }
    else
    {
        subBox[0] = Box(x0, x1, y0, y1, z0, zm);
        subBox[1] = Box(x0, x1, y0, y1, zm, z1);
    }

    return true;
}

bool Box::subdivide4(Box *subBox, double threshold)
{
    Box tmpSubBox[2];
    subdivide(tmpSubBox, threshold);
    return tmpSubBox[0].subdivide(subBox, threshold) && tmpSubBox[1].subdivide(subBox+2, threshold);
}

bool Box::isSmall(double threshold)
{
    return (x1 - x0 < threshold && y1 - y0 < threshold && z1 - z0 < threshold);
}

bool Box::isPtIn(double x, double y, double z)
{
    if (x >= x0 && x <= x1
            && y >= y0 && y <= y1
            && z >= z0 && z <= z1)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool Box::contains(Box *box)
{
    return (x0 >= x0 && x1 <= x1 && y0 >= y0 && y1 <= y1
            && z0 >= z0 && z1 <= z1);
}

bool Box::isEmpty()
{
    return x1 < x0 || y1 < y0 || z1 < z0;
}

bool Box::isPoint()
{
    return x0 == x1 && y0 == y1 && z0 == z1;
}

Box Box::intersect(Box *box)
{
    Box ibox;
    ibox.x0 = std::max(x0, x0);
    ibox.x1 = std::min(x1, x1);
    ibox.y0 = std::max(y0, y0);
    ibox.y1 = std::min(y1, y1);
    ibox.z0 = std::max(z0, z0);
    ibox.z1 = std::min(z1, z1);

    return ibox;
}

Box Box::plus(Box *box)
{
    Box iBox;
    iBox.x0 = std::min(x0, x0);
    iBox.x1 = std::max(x1, x1);
    iBox.y0 = std::min(y0, y0);
    iBox.y1 = std::max(y1, y1);
    iBox.z0 = std::min(z0, z0);
    iBox.z1 = std::max(z1, z1);

    return iBox;
}

bool Box::intersect(double tri[3][2])
{
    double p[4][2] = {{x0, y0}, {x1, y0}, {x1, y1}, {x0, y1}};
    if (isPtIn(tri[0][0], tri[0][1])
            || isPtIn(tri[1][0], tri[1][1])
            || isPtIn(tri[2][0], tri[2][1]))
    {
        return true;
    }
    else if (ptInTriangle(p[0], tri) || ptInTriangle(p[1], tri) || ptInTriangle(p[2], tri)
             || ptInTriangle(p[3], tri))
    {
        return true;
    }
    else if (intersect(tri[0], tri[1])
             || intersect(tri[1], tri[2])
             || intersect(tri[2], tri[0]))
    {
        return true;
    }

    return false;
}

bool Box::intersect(double *p0, double *p1)
{
    double p[4][2] = {{x0, y0}, {x1, y0}, {x1, y1}, {x0, y1}};

    if (segmentIntersect(p[0], p[1], p0, p1)
            || segmentIntersect(p[1], p[2], p0, p1)
            || segmentIntersect(p[2], p[3], p0, p1)
            || segmentIntersect(p[3], p[0], p0, p1))
    {
        return true;
    }

    return false;
}

void Box::getCenter(double *pt)
{
    pt[0] = (x0 + x1)/2;
    pt[1] = (y0 + y1)/2;
    pt[2] = (z0 + z1)/2;
}

void Box::getCenter(Point &pt)
{
    pt.x() = (x0 + x1)/2;
    pt.y() = (y0 + y1)/2;
    pt.z() = (z0 + z1)/2;
}

double Box::radius()
{
    return sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2) + pow (z1 - z0, 2))/2;
}

void Box::getCornerPts(double pt[8][3], int &num)
{
    double delta[3] = {x1 - x0, y1 - y0, z1 - z0};
    if (delta[0] < epsilon && delta[1] < epsilon && delta[2] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        num = 1;
    }
    else if (delta[0] < epsilon && delta[1] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x0;        pt[1][1] = y0;        pt[1][2] = z1;
        num = 2;
    }
    else if (delta[0] < epsilon && delta[2] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x0;        pt[1][1] = y1;        pt[1][2] = z0;
        num = 2;
    }
    else if (delta[1] < epsilon && delta[2] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x1;        pt[1][1] = y0;        pt[1][2] = z0;
        num = 2;
    }
    else if (delta[0] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x0;        pt[1][1] = y1;        pt[1][2] = z0;
        pt[1][0] = x0;        pt[1][1] = y1;        pt[1][2] = z1;
        pt[1][0] = x0;        pt[1][1] = y0;        pt[1][2] = z1;
        num = 4;
    }
    else if (delta[1] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x1;        pt[1][1] = y0;        pt[1][2] = z0;
        pt[1][0] = x1;        pt[1][1] = y0;        pt[1][2] = z1;
        pt[1][0] = x0;        pt[1][1] = y0;        pt[1][2] = z1;
        num = 4;
    }
    else if (delta[2] < epsilon)
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x0;        pt[1][1] = y1;        pt[1][2] = z0;
        pt[1][0] = x1;        pt[1][1] = y1;        pt[1][2] = z0;
        pt[1][0] = x1;        pt[1][1] = y0;        pt[1][2] = z0;
        num = 4;
    }
    else
    {
        pt[0][0] = x0;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[0][0] = x0;        pt[0][1] = y1;        pt[0][2] = z0;
        pt[0][0] = x1;        pt[0][1] = y1;        pt[0][2] = z0;
        pt[0][0] = x1;        pt[0][1] = y0;        pt[0][2] = z0;
        pt[1][0] = x0;        pt[1][1] = y0;        pt[1][2] = z1;
        pt[1][0] = x0;        pt[1][1] = y1;        pt[1][2] = z1;
        pt[1][0] = x1;        pt[1][1] = y1;        pt[1][2] = z1;
        pt[1][0] = x1;        pt[1][1] = y0;        pt[1][2] = z1;
        num = 8;
    }
}

bool Box::isPtOnBoundary(double x, double y, double z)
{
    return isPtIn(x, y) && (x == x0 || x == x1 || y == y0 || y == y1 || z == z0 || z == z1);
}

double Box::cross(double *p0, double *p1, double *p2)
{
    double u[2] = {p1[0] - p0[0], p1[1] - p0[1]};
    double v[2] = {p2[0] - p0[0], p2[1] - p0[1]};

    return u[0] * v[1] - u[1] * v[0];
}

bool Box::ptInTriangle(double *pt, double tri[3][2])
{
    int delta = (cross(tri[0], tri[1], pt) >= 0 ? 1 : -1);
    for (int i = 1; i < 3; i++)
    {
        int j = i + 1;
        if (j == 3)
        {
            j = 0;
        }
        if (delta != (cross(tri[i], tri[j], pt) >= 0 ? 1 : -1))
        {
            return false;
        }
    }

    return true;
}

bool Box::segmentIntersect(double p0[], double p1[], double p2[], double p3[])
{
    double a[2] = {p1[0] - p0[0], p1[1] - p0[1]};
    double b[2] = {p3[0] - p2[0], p3[1] - p2[1]};
    double c[2] = {p2[0] - p0[0], p2[1] - p0[1]};

    double deno = a[0] * b[1] - a[1] * b[0];

    if (deno == 0)
    {
        return false;
    }

    double s = (c[0] * b[1] - c[1] * b[0])/deno;
    double t = (c[0] * a[1] - c[1] * a[0])/deno;

    if (0 <= s && s <= 1 && 0 <= t && t <= 1)
    {
        return true;
    }

    return false;
}
